package com.example.flower_shop.websocket;

import cn.hutool.json.JSONUtil;
import com.example.flower_shop.exception.exception;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * websocket方法接口实现类
 */
@Service
@Slf4j
public class WebSocketImpl implements WebSocket {
    private final ConcurrentHashMap<String, WebSocketSession> userPools = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, WebSocketSession> adminPools = new ConcurrentHashMap<>();
    private final ConcurrentLinkedQueue<String> idleAdminQueue = new ConcurrentLinkedQueue<>();
    private final ConcurrentLinkedQueue<String> userQueue = new ConcurrentLinkedQueue<>();
    private final ConcurrentHashMap<String, String> adminUserMapping = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, String> userAdminMapping = new ConcurrentHashMap<>();

    @Override
    public void handleOpen(WebSocketSession session) {
        Map<String, Object> attributes = session.getAttributes();
        String userId = (String) attributes.get("userId");
        Boolean isAdmin = (Boolean) attributes.get("isAdmin");
        if (isAdmin && !adminPools.containsKey(userId)) {
            adminPools.put(userId, session);
            boolean isIdle = true;
            while (!userQueue.isEmpty()) {
                String uid = userQueue.poll();
                WebSocketSession userSession = userPools.get(uid);
                if (userSession != null && userSession.isOpen()) {
                    // 如果客服上线后有用户排队，则建立连接，发送客户id给用户，发送用户id给客服
                    MessageReq userMsg = new MessageReq("", uid, userId, "system");
                    sendMessage(userSession, JSONUtil.toJsonStr(userMsg));
                    // 发给管理员自己的
                    MessageReq adminMsg = new MessageReq("", userId, uid, "system");
                    sendMessage(session, JSONUtil.toJsonStr(adminMsg));
                    adminUserMapping.putIfAbsent(userId, uid);
                    userAdminMapping.putIfAbsent(uid, userId);
                    isIdle = false;
                    break;
                }
            }
            if (isIdle) {
                idleAdminQueue.offer(userId);
            }
        } else if (!isAdmin && !userPools.containsKey(userId)) {
            userPools.put(userId, session);
            boolean isIdle = true;
            while (!idleAdminQueue.isEmpty()) {
                String adminId = idleAdminQueue.poll();
                WebSocketSession adminSession = adminPools.get(adminId);
                if (adminSession != null) {
                    // 发给管理员的
                    MessageReq adminMsg = new MessageReq("", adminId, userId, "system");
                    sendMessage(adminSession, JSONUtil.toJsonStr(adminMsg));
                    // 发给用户自己的
                    MessageReq userMsg = new MessageReq("", userId, adminId, "system");
                    sendMessage(session, JSONUtil.toJsonStr(userMsg));
                    userAdminMapping.putIfAbsent(userId, adminId);
                    adminUserMapping.putIfAbsent(adminId, userId);
                    isIdle = false;
                    break;
                }
            }
            if (isIdle) {
                if (!userQueue.isEmpty()) {
                    MessageReq userMsg = new MessageReq("正在排队中，前方有:" + userQueue.size() + "人", "", "", "normal");
                    sendMessage(session, JSONUtil.toJsonStr(userMsg));
                }
                userQueue.offer(userId);
            }
        }
    }

    @Override
    public void handleClose(WebSocketSession session) {
        Map<String, Object> attributes = session.getAttributes();
        Boolean isAdmin = (Boolean) attributes.get("isAdmin");
        String userId = (String) attributes.get("userId");
        log.warn("用户" + userId + isAdmin + "断开连接");
        WebSocketSession remove = null;
        if (isAdmin) {
            remove = adminPools.remove(userId);
            idleAdminQueue.remove(userId);
            userQueue.offer(adminUserMapping.get(userId));
        } else {
            remove = userPools.remove(userId);
            userQueue.remove(userId);
            idleAdminQueue.offer(userAdminMapping.get(userId));
        }
        log.info(adminPools.toString());
        log.info(userPools.toString());
        if (remove != null && remove.isOpen()) {
            try {
                remove.close();
            } catch (IOException e) {
                throw new exception(e);
            }
        }
    }

    @Override
    public void HandleError(WebSocketSession session, Throwable error) {
        log.warn("发生异常：" + error.getMessage());
        handleClose(session);
        throw new exception(error);
    }

    @Override
    public void handleMessage(WebSocketSession session, String message) {
        String msg = String.format("收到来自%s的消息：%s", session.getRemoteAddress(), message);
        log.info(msg);
    }

    @Override
    public void sendMessage(WebSocketSession session, String message) {
        if (session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(message));
            } catch (IOException e) {
                throw new exception(e);
            }
        }
    }

    @Override
    public void sendMessage(String userId, TextMessage message) {
        WebSocketSession session = userPools.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(message);
            } catch (IOException e) {
                throw new exception(e);
            }
        }
    }

    @Override
    public void sendMessage(String userId, String message) {
        this.sendMessage(userId, new TextMessage(message));
    }

    /**
     * 广播消息
     *
     * @param message 消息
     */
    @Override
    public void broadcast(String message) {
        this.broadcast(new TextMessage(message));
    }

    /**
     * 广播消息
     *
     * @param message 消息
     */
    @Override
    public void broadcast(TextMessage message) {
        for (WebSocketSession session : userPools.values()) {
            if (session.isOpen()) {
                try {
                    session.sendMessage(message);
                } catch (IOException e) {
                    throw new exception(e);
                }
            }
        }
    }

    @Override
    public void sendMessage(MessageReq messageReq) {
        if (StringUtils.isBlank(messageReq.getSender())) {
            return;
        }
        if (messageReq.getType().equals("close")) {
            // 如果是用户结束 那么就把管理员放回队列
            if (userPools.containsKey(messageReq.getSender())) {
                closeSession(userPools.remove(messageReq.getSender()));
                idleAdminQueue.offer(messageReq.getReceiver());
            } else {
                closeSession(adminPools.remove(messageReq.getSender()));
                userQueue.offer(messageReq.getReceiver());
            }
        } else {
            // 发送人是用户 就是发给管理员的
            if (userPools.containsKey(messageReq.getSender())) {
                WebSocketSession session = adminPools.get(messageReq.getReceiver());
                MessageReq userMsg = new MessageReq("对方不在线，消息发送失败", messageReq.getSender(), messageReq.getReceiver(), "normal");
                if (session != null && session.isOpen()) {
                    userMsg.setMessage(messageReq.getMessage());
                    sendMessage(session, JSONUtil.toJsonStr(userMsg));
                } else {
                    sendMessage(userPools.get(messageReq.getSender()), JSONUtil.toJsonStr(userMsg));
                }
            } else {
                WebSocketSession session = userPools.get(messageReq.getReceiver());
                MessageReq adminMsg = new MessageReq(messageReq.getMessage(), messageReq.getReceiver(), messageReq.getSender(), "normal");
                if (session != null && session.isOpen()) {
                    sendMessage(session, JSONUtil.toJsonStr(adminMsg));
                }
            }
        }
    }

    private void closeSession(WebSocketSession session) {
        if (session != null && session.isOpen()) {
            try {
                session.close();
            } catch (IOException e) {
                throw new exception(e);
            }
        }
    }

    /**
     * 给包含在这个集合中的用户发消息
     *
     * @param userIds ID集合
     * @param message 消息
     */
    @Override
    public void sendMessagesToIncludedUsers(Set<String> userIds, String message) {
        for (Map.Entry<String, WebSocketSession> entry : userPools.entrySet()) {
            if (userIds.contains(entry.getKey())) {
                WebSocketSession value = entry.getValue();
                if (value != null && value.isOpen()) {
                    try {
                        value.sendMessage(new TextMessage(message));
                    } catch (IOException e) {
                        throw new exception(e);
                    }
                }
            }
        }
    }

    /**
     * 给不包含在这个集合中的用户发消息
     *
     * @param userIds ID集合
     * @param message 消息
     */
    @Override
    public void sendMessagesToExcludedUsers(Set<String> userIds, String message) {
        for (Map.Entry<String, WebSocketSession> entry : userPools.entrySet()) {
            if (!userIds.contains(entry.getKey())) {
                WebSocketSession value = entry.getValue();
                if (value != null && value.isOpen()) {
                    try {
                        value.sendMessage(new TextMessage(message));
                    } catch (IOException e) {
                        throw new exception(e);
                    }
                }
            }
        }
    }
}
